﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.Distribution
{
    /// <summary>
    /// This class represents a Uniform distribution.
    /// </summary>
    [Serializable]
    public class UniformDistribution
    {
        /// <summary>
        /// The interval of the distribution.
        /// </summary>
        private readonly ClosedInterval interval;

        /// <summary>
        /// Initializes a new instance of the <see cref="UniformDistribution"/> class.
        /// </summary>
        /// <param name="a">The first parameter of the distribution.</param>
        /// <param name="b">The second parameter of the distribution.</param>
        public UniformDistribution(double a, double b)
        {
            if (b < a)
            {
                ExtendedMath.Swap(ref a, ref b);
            }

            this.interval = new ClosedInterval(a, b);
        }

        /// <summary>
        /// Gets the A parameter of the distribution.
        /// </summary>
        /// <value>The A parameter of the distribution.</value>
        public double A
        {
            get { return this.interval.Start; }
        }

        /// <summary>
        /// Gets the B parameter of the distribution.
        /// </summary>
        /// <value>The B parameter of the distribution.</value>
        public double B
        {
            get { return this.interval.End; }
        }

        /// <summary>
        /// Gets the mean of the distribution.
        /// </summary>
        /// <value>The mean of the distribution.</value>
        public double Mean
        {
            get { return 0.5*(this.interval.Start + this.interval.End); }
        }

        /// <summary>
        /// Gets the median of the distribution.
        /// </summary>
        /// <value>The median of the distribution.</value>
        public double Median
        {
            get { return 0.5*(this.interval.Start + this.interval.End); }
        }

        /// <summary>
        /// Gets the mode of the distribution.
        /// </summary>
        /// <value>The mode of the distribution.</value>
        public double Mode
        {
            get { return 0.5*(this.interval.Start + this.interval.End); }
        }

        /// <summary>
        /// Gets the variance of the distribution.
        /// </summary>
        /// <value>The variance of the distribution.</value>
        public double Variance
        {
            get { return 1.0/12.0*Math.Pow(this.interval.End - this.interval.Start, 2); }
        }

        /// <summary>
        /// Gets the skewness of the distribution.
        /// </summary>
        /// <value>The skewness of the distribution.</value>
        public double Skewness
        {
            get { return 0; }
        }

        /// <summary>
        /// Computes the probability mass function of the Uniform distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The probability mass function of the Uniform distribution at the specified postion x.</returns>
        public double ProbabilityMassFunction(double x)
        {
            if (this.interval.IsInInterval(x))
            {
                return 1/(this.interval.End - this.interval.Start);
            }

            return 0;
        }

        /// <summary>
        /// Computes the cumulative distribution function of the Uniform distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The cumulative distribution function of the Uniform distribution at the specified postion x.</returns>
        public double CumulativeDistributionFunction(double x)
        {
            if (x <= this.interval.Start)
            {
                return 0;
            }

            if (x >= this.interval.End)
            {
                return 1;
            }

            return (x - this.interval.Start)/(this.interval.End - this.interval.Start);
        }
    }
}